package chap_03_ThreadLocal;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description ThreadLocal 学习：remove 关于 线程池 & 内存泄漏 问题
 * @Author shihan
 * @Date 2022/11/5 10:36
 * @Version 1.0
 */
public class ThreadLocal_demo_02_remove {

    public static ThreadLocal<Integer> myThreadLocalWithInitial = ThreadLocal.withInitial(() -> 100);

    /**
     * 除了 ThreadFactory 用默认的就够了，其他几个参数都很重要而且有说法：
     * BlockingQueue：到达 maximumPoolSize 后的阻塞队列类型和长度
     * RejectedExecutionHandler：maximumPoolSize + BlockingQueue 满了之后，触发的处理策略（默认抛异常的拒绝策略）
     */
    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            5,
            Runtime.getRuntime().availableProcessors() + 1,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10),
            new ThreadPoolExecutor.AbortPolicy()
    );

    /**
     * 1、使用线程池的情况下，ThreadLocal 在当前线程结束前不 remove 很容易出数据错乱问题
     * 2、其次在每个线程的末尾不进行 remove，时间一长会产生内存泄漏，导致 OOM 问题
     * 3、优雅地 remove 是在线程的末尾，并且需要用 try-catch-finally 进行包裹的 finally 中处理，因为有可能会出现异常
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("处理器个数：" + Runtime.getRuntime().availableProcessors());

        for (int i = 0; i < 5; i++) {
            threadPool.execute(
                    () -> {
                        System.out.println(Thread.currentThread().getName() + " before " + myThreadLocalWithInitial.get()); // 100
                        myThreadLocalWithInitial.set(500);
                        System.out.println(Thread.currentThread().getName() + " after " + myThreadLocalWithInitial.get()); // 500
                    }
            );
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 5; i++) {
            threadPool.execute(
                    () -> {
                        try {
                            System.out.println(Thread.currentThread().getName() + " current " + myThreadLocalWithInitial.get()); // 500
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            // 优雅地 remove 是在线程的末尾，并且需要用 try-catch-finally 进行包裹的 finally 中处理，因为有可能会出现异常
                            myThreadLocalWithInitial.remove();
                        }
                    }
            );
        }

        // 停止 main 线程必须要 shutdown 线程池
        threadPool.shutdown();
    }

}
